package jdos.ints;

import jdos.Dosbox;
import jdos.hardware.IoHandler;
import jdos.hardware.Memory;
import jdos.hardware.VGA;
import jdos.misc.Log;
import jdos.types.LogSeverities;
import jdos.types.LogTypes;

public class Int10_char {

    static void VGA_CopyRow(/*Bit8u*/short cleft,/*Bit8u*/short cright,/*Bit8u*/short rold,/*Bit8u*/short rnew,/*PhysPt*/int  base) {
        /*PhysPt*/int  src,dest;/*Bitu*/int copy;
        /*Bit8u*/int cheight = Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CHAR_HEIGHT);
        dest=base+8*((Int10_modes.CurMode.twidth*rnew)*cheight+cleft);
        src=base+8*((Int10_modes.CurMode.twidth*rold)*cheight+cleft);
        /*Bitu*/int nextline=8*Int10_modes.CurMode.twidth;
        /*Bitu*/int rowsize=8*(cright-cleft);
        copy=cheight;
        for (;copy>0;copy--) {
            for (/*Bitu*/int x=0;x<rowsize;x++) Memory.mem_writeb(dest+x,Memory.mem_readb(src+x));
            dest+=nextline;src+=nextline;
        }
    }

    static void VGA_FillRow(/*Bit8u*/short cleft,/*Bit8u*/short cright,/*Bit8u*/short row,/*PhysPt*/int  base,/*Bit8u*/short attr) {
        /* Write some bytes */
        /*Bit8u*/int cheight = Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CHAR_HEIGHT);
        /*PhysPt*/int  dest=base+8*((Int10_modes.CurMode.twidth*row)*cheight+cleft);
        /*Bitu*/int nextline=8*Int10_modes.CurMode.twidth;
        /*Bitu*/int copy = cheight;/*Bitu*/int rowsize=8*(cright-cleft);
        for (;copy>0;copy--) {
            for (/*Bitu*/int x=0;x<rowsize;x++) Memory.mem_writeb(dest+x,attr);
            dest+=nextline;
        }
    }

    static public void INT10_ScrollWindow(/*Bit8u*/short rul,/*Bit8u*/short cul,/*Bit8u*/short rlr,/*Bit8u*/short clr,/*Bit8s*/byte nlines,/*Bit8u*/short attr,/*Bit8u*/short page) {
/* Do some range checking */
        page=0xff;
        /*BIOS_NCOLS*//*Bit16u*/int ncols=Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_COLS);
        /*BIOS_NROWS*//*Bit16u*/int nrows=Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_ROWS)+1;
        if(rul>rlr) return;
        if(cul>clr) return;
        if(rlr>=nrows) rlr=(short)(nrows-1);
        if(clr>=ncols) clr=(short)(ncols-1);
        clr++;

        /* Get the correct page */
        if(page==0xFF) page=(short)Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURRENT_PAGE);
        /*PhysPt*/int  base=Int10_modes.CurMode.pstart+page*Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_PAGE_SIZE);

        /* See how much lines need to be copied */
        /*Bit8u*/int start=0,end=0;/*Bits*/int next=0;
        /* Copy some lines */
        boolean gotofilling = false;
        if (nlines>0) {
            start=(rlr-nlines+1) & 0xFF;
            end=rul;
            next=-1;
        } else if (nlines<0) {
            start=(rul-nlines-1) & 0xFF;
            end=rlr;
            next=1;
        } else {
            nlines=(byte)(rlr-rul+1);
            //goto filling;
            gotofilling = true;
        }
        if (!gotofilling) {
            while (start!=end) {
                start=(start+next) & 0xFF;
                VGA_CopyRow(cul,clr,(short)start,(short)((start+nlines) & 0xFF),base);

            }
        }
        /* Fill some lines */
    //filling:
        if (nlines>0) {
            start=rul;
        } else {
            nlines=(byte)-nlines;
            start=rlr-nlines+1;
        }
        for (;nlines>0;nlines--) {
            VGA_FillRow(cul,clr,(short)start,base,attr);
            start++;
        }
    }

    static public void INT10_SetCursorPos(/*Bit8u*/short row,/*Bit8u*/short col,/*Bit8u*/short page) {
        /*Bit16u*/int address;

        if (page>7) if (Log.level<=LogSeverities.LOG_ERROR) Log.log(LogTypes.LOG_INT10,LogSeverities.LOG_ERROR,"INT10_SetCursorPos page "+page);
        // Bios cursor pos
        Memory.real_writeb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURSOR_POS+page*2,col);
        Memory.real_writeb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURSOR_POS+page*2+1,row);
        // Set the hardware cursor
        /*Bit8u*/int current=Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURRENT_PAGE);
        if(page==current) {
            // Get the dimensions
            /*BIOS_NCOLS*//*Bit16u*/int ncols=Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_COLS);
            // Calculate the address knowing nbcols nbrows and page num
            // NOTE: Int10.BIOSMEM_CURRENT_START counts in colour/flag pairs
            address=(ncols*row)+col+Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURRENT_START)/2;
            // CRTC regs 0x0e and 0x0f
            /*Bit16u*/int base=Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CRTC_ADDRESS);
            IoHandler.IO_Write(base,0x0e);
            IoHandler.IO_Write(base+1,(address>>8));
            IoHandler.IO_Write(base,0x0f);
            IoHandler.IO_Write(base+1,address);
        }
    }

    static public int ReadCharAttr(/*Bit16u*/int col,/*Bit16u*/int row,/*Bit8u*/short page) {
        /* Externally used by the mouse routine */
        /*PhysPt*/int  fontdata;
        /*Bitu*/int x,y;
        /*Bit8u*/int cheight = Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CHAR_HEIGHT);
        boolean split_chr = false;

            for(/*Bit16u*/int chr=0;chr <= 255 ;chr++) {
                if (!split_chr || (chr<128)) fontdata = Memory.Real2Phys(Memory.RealGetVec(0x43))+chr*cheight;
                else fontdata = Memory.Real2Phys(Memory.RealGetVec(0x1F))+(chr-128)*cheight;

                x=8*col;
                y=cheight*row;
                boolean error=false;
                for (/*Bit8u*/short h=0;h<cheight;h++) {
                    /*Bit8u*/short bitsel=128;
                    /*Bit8u*/int bitline=Memory.mem_readb(fontdata++);
                    /*Bit8u*/short res=0;
                    /*Bit8u*/short vidline=0;
                    /*Bit16u*/int tx=x;
                    while (bitsel!=0) {
                        //Construct bitline in memory
                        res = Int10_put_pixel.INT10_GetPixel(tx,y,page);
                        if(res!=0) vidline|=bitsel;
                        tx++;
                        bitsel>>=1;
                    }
                    y++;
                    if(bitline != vidline){
                        /* It's not character 'chr', move on to the next */
                        error = true;
                        break;
                    }
                }
                if(!error){
                    /* We found it */
                    return chr;
                }
            }
            Log.log(LogTypes.LOG_INT10,LogSeverities.LOG_ERROR,"ReadChar didn't find character");
            return 0;
    }

    public static int INT10_ReadCharAttr(/*Bit8u*/short page) {
        if(page==0xFF) page=(short)Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURRENT_PAGE);
        /*Bit8u*/short cur_row=Int10.CURSOR_POS_ROW(page);
        /*Bit8u*/short cur_col=Int10.CURSOR_POS_COL(page);
        return ReadCharAttr(cur_col,cur_row,page);
    }

    static private boolean warned_use = false;
    public static void WriteChar(/*Bit16u*/int col,/*Bit16u*/int row,/*Bit8u*/short chr,/*Bit8u*/short attr,boolean useattr) {
        /* Externally used by the mouse routine */
        /*RealPt*/int fontdata;
        /*Bitu*/int x,y;
        /*Bit8u*/int cheight = Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CHAR_HEIGHT);
            fontdata=Memory.RealGetVec(0x43);
            fontdata=Memory.RealMake(Memory.RealSeg(fontdata), Memory.RealOff(fontdata) + chr*cheight);

        if(!useattr) { //Set attribute(color) to a sensible value

            if(!warned_use){
                if (Log.level<=LogSeverities.LOG_ERROR) Log.log(LogTypes.LOG_INT10,LogSeverities.LOG_ERROR,"writechar used without attribute in non-textmode "+String.valueOf((char)chr)+" "+Integer.toString(chr,16));
                warned_use = true;
            }

                attr = 0xf;

        }

        //Some weird behavior of mode 6 (and 11)
        if ((Int10_modes.CurMode.mode == 0x6)/* || (Int10_modes.CurMode.mode==0x11)*/) attr = (short)((attr&0x80)|1);
        //(same fix for 11 fixes vgatest2, but it's not entirely correct according to wd)

        x=8*col;
        y=cheight*row;/*Bit8u*/short xor_mask=0x0;

        for (/*Bit8u*/short h=0;h<cheight;h++) {
            /*Bit8u*/short bitsel=128;
            /*Bit8u*/int bitline = Memory.mem_readb(Memory.Real2Phys( fontdata ));
            fontdata = Memory.RealMake( Memory.RealSeg( fontdata ), Memory.RealOff( fontdata ) + 1);
            /*Bit16u*/int tx=x;
            while (bitsel!=0) {
                if ((bitline&bitsel)!=0) Int10_put_pixel.INT10_PutPixel(tx,y,attr);
                else Int10_put_pixel.INT10_PutPixel(tx,y,(short)(attr & xor_mask));
                tx++;
                bitsel>>=1;
            }
            y++;
        }
    }

    public static void INT10_WriteChar(/*Bit8u*/short chr,/*Bit8u*/short attr,/*Bit8u*/short page,/*Bit16u*/int count,boolean showattr) {

            showattr=true; //Use attr in graphics mode always

                //case MachineType.MCH_VGA:
                page%=Int10_modes.CurMode.ptotal;

        /*Bit8u*/short cur_row=Int10.CURSOR_POS_ROW(page);
        /*Bit8u*/short cur_col=Int10.CURSOR_POS_COL(page);
        /*BIOS_NCOLS*//*Bit16u*/int ncols=Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_COLS);
        while (count>0) {
            WriteChar(cur_col,cur_row,chr,attr,showattr);
            count--;
            cur_col++;
            if(cur_col==ncols) {
                cur_col=0;
                cur_row++;
            }
        }
    }

    static void INT10_TeletypeOutputAttr(/*Bit8u*/short chr,/*Bit8u*/short attr,boolean useattr,/*Bit8u*/short page) {
        /*BIOS_NCOLS*//*Bit16u*/int ncols=Memory.real_readw(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_COLS);
        /*BIOS_NROWS*//*Bit16u*/int nrows=Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_NB_ROWS)+1;
        /*Bit8u*/short cur_row=Int10.CURSOR_POS_ROW(page);
        /*Bit8u*/short cur_col=Int10.CURSOR_POS_COL(page);
        switch (chr) {
        case 7:
        //TODO BEEP
        break;
        case 8:
            if(cur_col>0) cur_col--;
            break;
        case '\r':
            cur_col=0;
            break;
        case '\n':
//		cur_col=0; //Seems to break an old chess game
            cur_row++;
            break;
        case '\t':
            do {
                INT10_TeletypeOutputAttr((short)' ',attr,useattr,page);
                cur_row=Int10.CURSOR_POS_ROW(page);
                cur_col=Int10.CURSOR_POS_COL(page);
            } while((cur_col%8) != 0);
            break;
        default:
            /* Draw the actual Character */
            WriteChar(cur_col,cur_row,chr,attr,useattr);
            cur_col++;
        }
        if(cur_col==ncols) {
            cur_col=0;
            cur_row++;
        }
        // Do we need to scroll ?
        if(cur_row==nrows) {
            //Fill with black on non-text modes and with 0x7 on textmode
            /*Bit8u*/short fill = 0;
            INT10_ScrollWindow((short)0,(short)0,(short)(nrows-1),(short)(ncols-1),(byte)-1,fill,page);
            cur_row--;
        }
        // Set the cursor for the page
        INT10_SetCursorPos(cur_row,cur_col,page);
    }

    public static void INT10_TeletypeOutputAttr(/*Bit8u*/int chr,/*Bit8u*/int attr,boolean useattr) {
        INT10_TeletypeOutputAttr((short)chr,(short)attr,useattr,(short)Memory.real_readb(Int10.BIOSMEM_SEG,Int10.BIOSMEM_CURRENT_PAGE));
    }

    public static void INT10_TeletypeOutput(/*Bit8u*/int chr,/*Bit8u*/int attr) {
        INT10_TeletypeOutputAttr((short)chr,(short)attr,true);
    }

}
